package cn.takujo.simple.elasticsearch.client;

import java.io.IOException;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;

import com.fasterxml.jackson.databind.ObjectMapper;

import cn.takujo.simple.elasticsearch.client.MappingBuilder.MetaProperty;
import lombok.Getter;

@Getter
public class EsRest {

	private String hostname;
	private int port;
	private String scheme;

	public static final String HTTP_GET = "GET";
	public static final String HTTP_PUT = "PUT";
	public static final String HTTP_DELETE = "DELETE";
	public static final String HTTP_POST = "POST";
	public static final String HTTP_HEAD = "HEAD";

	public RestClient client;

	public EsRest(String hostname, int port, String scheme) {
		this.hostname = hostname;
		this.port = port;
		this.scheme = scheme;
		client = RestClient.builder(new HttpHost(hostname, port, scheme)).build();
	}

	/**
	 * 保存一个文档
	 * 
	 * @param index
	 *            索引
	 * @param id
	 *            文档唯一标识
	 * @param source
	 *            保存字段内容
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response saveOneDoc(String index, Integer id, Map<String, Object> source) throws IOException {
		judgeIndex(index);
		Request req = new Request(HTTP_PUT, "/" + index + "/doc/" + id);
		ObjectMapper objectMapper = new ObjectMapper();
		String entity = objectMapper.writeValueAsString(source);
		req.setJsonEntity(entity);
		return client.performRequest(req);
	}

	/**
	 * 通过id删除唯一文档
	 * 
	 * @param index
	 *            索引
	 * @param id
	 *            文档id
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response deleteDocById(String index, Integer id) throws IOException {
		judgeIndex(index);
		Request req = new Request(HTTP_DELETE, "/" + index + "/doc/" + id);
		return client.performRequest(req);
	}

	/**
	 * 通过id获取唯一文档
	 * 
	 * @param index
	 *            索引
	 * @param id
	 *            文档id
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response getDocById(String index, Integer id) throws IOException {
		judgeIndex(index);
		Request req = new Request(HTTP_GET, "/" + index + "/doc/" + id);
		return client.performRequest(req);
	}

	/**
	 * 删除索引
	 * 
	 * @param index
	 *            索引
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response deleteIndex(String index) throws IOException {
		judgeIndex(index);
		Request request = new Request(HTTP_DELETE, "/" + index);
		return client.performRequest(request);
	}

	/**
	 * 索引是否存在
	 * 
	 * @param index
	 *            索引
	 * @return true即为存在，false不存在
	 * @throws IOException
	 *             失败则产生异常
	 */
	public boolean indexExist(String index) throws IOException {
		Request request = new Request(HTTP_HEAD, "/" + index);
		Response resp = client.performRequest(request);
		return is200(resp);
	}

	/**
	 * 创建索引
	 * 
	 * @param shards
	 *            切片数量
	 * @param replicas
	 *            备份数量
	 * @param index
	 *            索引
	 * @param metas
	 *            映射类型（名固定为doc）
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response createIndex(Integer shards, Integer replicas, String index, Map<String, MetaProperty> metas)
			throws IOException {
		if (shards == null || shards <= 0) {
			shards = 1;
		}
		if (replicas == null || replicas < 0) {
			replicas = 0;
		}
		MappingBuilder builder = new MappingBuilder();
		String entity = "{" + "\"settings\" : {" + "\"index\" : {" + "\"number_of_shards\" : " + shards + ","
				+ "\"number_of_replicas\" : " + replicas + "}" + "}," + builder.build(metas) + "}";
		builder = null;
		Request request = new Request(HTTP_PUT, "/" + index);
		request.setJsonEntity(entity);
		return client.performRequest(request);
	}

	private void judgeIndex(String index) {
		try {
			if (!indexExist(index)) {
				throw new RuntimeException(index + "索引不存在");
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException("查询" + index + "索引失败");
		}
	}

	public boolean is200(Response resp) {
		int statusCode = resp.getStatusLine().getStatusCode();
		return statusCode == 200;
	}

	/**
	 * 关闭客户端
	 * 
	 * @throws IOException
	 *             失败则产生异常
	 */
	public void close() throws IOException {
		client.close();
	}

	/**
	 * 从所有中索引搜索
	 * 
	 * @param queryDsl
	 *            json请求体
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response search(String queryDsl) throws IOException {
		Request req = new Request(HTTP_GET, "/_search");
		if (queryDsl != null) {
			req.setJsonEntity(queryDsl);
		}
		return client.performRequest(req);
	}

	/**
	 * 在指定索引中搜索
	 * 
	 * @param index
	 *            索引
	 * @param queryDsl
	 *            json请求体
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response searchInIndex(String index, String queryDsl) throws IOException {
		judgeIndex(index);
		Request req = new Request(HTTP_GET, "/" + index + "/_search");
		if (queryDsl != null) {
			req.setJsonEntity(queryDsl);
		}
		return client.performRequest(req);
	}

	/**
	 * 在多个索引中搜索
	 * 
	 * @param indices
	 *            索引数组
	 * @param queryDsl
	 *            json请求体
	 * @return 响应结果
	 * @throws IOException
	 *             失败则产生异常
	 */
	public Response searchInIndices(String[] indices, String queryDsl) throws IOException {
		if (indices == null || indices.length == 0) {
			throw new RuntimeException("索引丢失，且索引数量必须大于1");
		}
		StringBuffer sb = new StringBuffer();
		for (String index : indices) {
			judgeIndex(index);
			sb.append(index + ",");
		}
		sb.deleteCharAt(sb.length() - 1);
		Request req = new Request(HTTP_GET, "/" + sb.toString() + "/_search");
		if (queryDsl != null) {
			req.setJsonEntity(queryDsl);
		}
		return client.performRequest(req);
	}

	/**
	 * count计数
	 * 
	 * @param indices
	 *            索引数组
	 * @param queryDsl
	 *            json请求体（只允许包含query）
	 * @return 返回命中数量
	 * @throws IOException
	 *             失败则产生异常
	 */
	public int countInIndices(String[] indices, String queryDsl) throws IOException {
		if (indices == null || indices.length == 0) {
			throw new RuntimeException("索引丢失，且索引数量必须大于1");
		}
		StringBuffer sb = new StringBuffer();
		for (String index : indices) {
			judgeIndex(index);
			sb.append(index + ",");
		}
		sb.deleteCharAt(sb.length() - 1);
		Request req = new Request(HTTP_GET, "/" + sb.toString() + "/_count");
		if (queryDsl != null) {
			req.setJsonEntity(queryDsl);
		}
		Response response = client.performRequest(req);
		HttpEntity entity = response.getEntity();
		Map<String, Object> trans = HttpEntityToMap.trans(entity);
		return (int) trans.get("count");
	}

	/**
	 * 全文搜索请求体json
	 * 
	 * @param body
	 *            全文搜索请求体
	 * @return json字符串
	 */
	public String fullTextDisMaxQueryDsl(FullTextSearchRequestBody body) {
		String query = body.getQuery();
		if (query == null) {
			throw new RuntimeException("FullTextSearchRequestBody query丢失");
		}
		String[] fieldsArr = body.getFields();
		if (fieldsArr == null || fieldsArr.length == 0) {
			throw new RuntimeException("FullTextSearchRequestBody fields丢失,且fields不能为空");
		}
		StringBuffer sb2 = new StringBuffer();
		for (String fs : fieldsArr) {
			sb2.append("\"" + fs + "\",");
		}
		sb2.deleteCharAt(sb2.length() - 1);
		String fields = sb2.toString();
		int from = body.getFrom();
		int size = body.getSize();
		String highlightPreTag = body.getHighlightPreTag();
		String highlightPostTag = body.getHighlightPostTag();
		int highlightFragmentSize = body.getHighlightFragmentSize();
		String[] highlightFieldsArr = body.getHighlightFields();
		String highlightFields = null;
		if (highlightFieldsArr == null || highlightFieldsArr.length == 0) {
			highlightFields = "\"*\": {}";
		} else {
			StringBuffer sb3 = new StringBuffer();
			for (String fs : highlightFieldsArr) {
				sb3.append("\"" + fs + "\": {},");
			}
			sb3.deleteCharAt(sb3.length() - 1);
			highlightFields = sb3.toString();
		}
		String queryDsl = "{" + "\"query\": {" + "\"dis_max\": {" + "\"queries\": [" + "{" + "\"multi_match\": {"
				+ "\"query\": \"" + query + "\"," + "\"fields\": [" + fields + "]" + "}" + "}" + "]" + "}" + "},"
				+ "\"from\": " + from + "," + "\"size\": " + size + "," + "\"highlight\": {" + "\"pre_tags\": [" + "\""
				+ highlightPreTag + "\"" + "]," + "\"post_tags\": [" + "\"" + highlightPostTag + "\"" + "],"
				+ "\"fragment_size\": " + highlightFragmentSize + "," + "\"fields\": {" + highlightFields + "}" + "}"
				+ "}";
		return queryDsl;
	}

	/**
	 * 全文搜索count请求体json
	 * 
	 * @param body
	 *            全文搜索请求体
	 * @return json字符串
	 */
	public String fullTextDisMaxCountQueryDsl(FullTextSearchRequestBody body) {
		String query = body.getQuery();
		if (query == null) {
			throw new RuntimeException("FullTextSearchRequestBody query丢失");
		}
		String[] fieldsArr = body.getFields();
		if (fieldsArr == null || fieldsArr.length == 0) {
			throw new RuntimeException("FullTextSearchRequestBody fields丢失,且fields不能为空");
		}
		StringBuffer sb2 = new StringBuffer();
		for (String fs : fieldsArr) {
			sb2.append("\"" + fs + "\",");
		}
		sb2.deleteCharAt(sb2.length() - 1);
		String Fields = sb2.toString();
		String queryDsl = "{" + "\"query\": {" + "\"dis_max\": {" + "\"queries\": [" + "{" + "\"multi_match\": {"
				+ "\"query\": \"" + query + "\"," + "\"fields\": [" + Fields + "]" + "}" + "}" + "]" + "}" + "}" + "}";
		return queryDsl;
	}

}
